package com.ethink.gateway.filter.pre;

import com.ethink.framework.common.constant.CommonConstant;
import com.ethink.framework.common.constant.CommonRespCode;
import com.ethink.framework.common.exception.BusinessException;
import com.ethink.framework.common.util.PathMatcherUtil;
import com.ethink.framework.common.web.context.RequestEntityType;
import com.ethink.gateway.constant.GatewayConstant;
import com.ethink.gateway.constant.GatewayRespCode;
import com.ethink.gateway.filter.AbstractPreFilter;
import com.ethink.gateway.handler.DefaultRequestHandler;
import com.ethink.gateway.handler.RequestHandler;
import com.ethink.gateway.handler.SystemRequestHandler;
import com.ethink.gateway.model.RequestEntityRule;
import com.ethink.gateway.properties.GateWayApiProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import javax.annotation.PostConstruct;
import java.util.List;


/**
 * 登录认证
 *
 * @author allen
 */
@Slf4j
@Component
public class AuthenticationFilter extends AbstractPreFilter {
    @Autowired
    private GateWayApiProperties apiProperties;
    @Autowired
    private SystemRequestHandler systemRequestHandler;
    @Autowired
    private DefaultRequestHandler defaultRequestHandler;

    @Override
    public int getOrder() {
        return GatewayConstant.FILTER_ORDER_AUTHENTICATION;
    }

    /**
     * 启动服务时执行，初始化请求的处理Handler加载到内存中
     */
    @PostConstruct
    public void init() {
        List<RequestEntityRule> requestEntityRules = apiProperties.getRequestEntityRules();
        if (CollectionUtils.isEmpty(requestEntityRules)) {
            log.error("网关路由未配置,系统无法正常提供服务");
            throw new BusinessException();
        }
    }

    @Override
    protected ServerWebExchange preFilter(ServerWebExchange exchange) {
        if (needAuthentication(exchange.getRequest())) {
            ServerHttpRequest mutateRequest = doAuthentication(exchange.getRequest());
            return exchange.mutate().request(mutateRequest).build();
        }
        return exchange;
    }

    /**
     * 是否需要登录认证，判断每个请求实体对应配置的免登录path是否匹配，匹配则免校验
     *
     * @param request
     * @return
     */
    private boolean needAuthentication(ServerHttpRequest request) {
        String path = request.getPath().value();
        for (RequestEntityRule requestEntityRule : apiProperties.getRequestEntityRules()) {
            // 便利请求对象类型配置的免登录配置
            if (PathMatcherUtil.match(requestEntityRule.getNoAuthPathRegex(), path)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 登录验证
     *
     * @return
     */
    private ServerHttpRequest doAuthentication(ServerHttpRequest request) {
        // 确认具体的请求实体类型
        // 先判断header 请求对象类型，
        // 没有类型，再根据header中的token参数对应类型
        // 最后通过path判断实际的类型,通过path判断，计算成本高
        RequestEntityRule requestEntityRule;
        RequestEntityType entityType = RequestEntityType.valueOfIgnoreCase(request.getHeaders().getFirst(CommonConstant.RequestHeader.REQUEST_ENTITY_TYPE));
        if (entityType == null) {
            // 通过path判断实际的类型
            requestEntityRule = getRequestEntityTypeByPath(request);
            entityType = requestEntityRule.getEntityType();
        } else {
            // 通过header已知类型，还需要验证请求对象类型和请求url是否在配置的url匹配
            requestEntityRule = getRequestEntityRule(entityType);
        }
        RequestHandler requestHandler = chooseHandler(entityType);
        // 具体登录认证逻辑交给对应handler
        return requestHandler.handler(request, requestEntityRule);
    }

    private RequestHandler chooseHandler(RequestEntityType entityType) {
        switch (entityType) {
            case SYSTEM_USER:
                return systemRequestHandler;
            default:
                return defaultRequestHandler;
        }
    }

    private RequestEntityType getRequestEntityTypeByHeader(ServerHttpRequest request) {
        // 先header参数判断请求对象类型，
        RequestEntityType entityType = RequestEntityType.valueOfIgnoreCase(request.getHeaders().getFirst(CommonConstant.RequestHeader.REQUEST_ENTITY_TYPE));
        if (entityType != null) {
            return entityType;
        }
        if (StringUtils.isNotBlank(request.getHeaders().getFirst(CommonConstant.RequestHeader.SYSTEM_TOKEN))) {
            return RequestEntityType.SYSTEM_USER;
        }
        return null;
    }

    private RequestEntityRule getRequestEntityTypeByPath(ServerHttpRequest request) {
        String path = request.getPath().value();
        for (RequestEntityRule requestEntityRule : apiProperties.getRequestEntityRules()) {
            if (PathMatcherUtil.match(requestEntityRule.getShouldFilterPathRegex(), path)) {
                return requestEntityRule;
            }
        }
        // 如果通过path无法匹配，这里需要返回403
        log.error("当前请求对象匿名访问未授权的请求地址 :{}", path);
        throw new BusinessException(CommonRespCode.ACCESS_DENIED);
    }

    private RequestEntityRule getRequestEntityRule(RequestEntityType entityType) {
        for (RequestEntityRule requestEntityRule : apiProperties.getRequestEntityRules()) {
            if (requestEntityRule.getEntityType() == entityType) {
                return requestEntityRule;
            }
        }
        throw new BusinessException(GatewayRespCode.RULE_MISS_ERROR);
    }

}